package twitter.TwitterLibrary;

import java.io.IOException;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.apache.log4j.*;

import twitter.Server.ServerType;

/**********************************************************
 * this class stores common methods 
 * @author fang
 *
 */
public class Utility {
	public static String task="";
	public static String result="";
	private final ReentrantReadWriteLock rwl ;
	private final Lock w;
	private final Lock r;
	public ArrayList electionResponseResult=new ArrayList();
	public ExecutorService threadPool;
	public Utility(ReentrantReadWriteLock rwl)
	{
		this.rwl=rwl;
		this.w=rwl.writeLock();
		this.r=rwl.readLock();
		threadPool=Executors.newCachedThreadPool();
	}
	public ReentrantReadWriteLock getLock()
	{
		return this.rwl;
	}
	/**************************************************************
	 * transfer xml document to string
	 * @param node
	 * @return
	 */
	public String xmlToString(Node node) {
		try {
			Source source = new DOMSource(node);
			StringWriter stringWriter = new StringWriter();
			Result result = new StreamResult(stringWriter);
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer();
			transformer.transform(source, result);
			return stringWriter.getBuffer().toString();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
		return null;
	}
/****************************************************
 * transfer string to xml document
 * @param xmlSource
 * @return
 * @throws SAXException
 * @throws IOException
 */
	public Document stringToDom(String xmlSource) throws SAXException,
			IOException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = null;
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("xmlSource: " + xmlSource);
		return builder.parse(new InputSource(new StringReader(xmlSource)));
	}
	/**************************************************
	 * get the hash tag string from a status string
	 * @param status
	 * @return
	 */
	public ArrayList<String> getHashflag(String status) {
		int start = 0;
		int end = 0;
		ArrayList<String> hashflags = new ArrayList<String>();

		String temp = status;
		while (temp.indexOf("%23") >= 0) {
			start = temp.indexOf("%23");
			end = temp.indexOf("+", start + 3);
			if (end != -1) {
				String hashflag = temp.substring(start + 3, end);
				hashflags.add(hashflag);
				temp = temp.substring(end + 1);

			} else {
				String hashflag = temp.substring(start + 3, temp.length());
				hashflags.add(hashflag);

				temp = "";

			}

		}
		return hashflags;
	}
	/***************************************
	 * set up the log4j
	 * @param logger
	 * @param file
	 */
	public void setLogger(Logger logger,String file)
	{
		FileAppender appender=new FileAppender();
		appender.setFile(file);
		appender.activateOptions();
		PatternLayout layout=new PatternLayout();
		layout.setConversionPattern("%-5p %t %d {yyyy-MM-dd HH:mm:ssS} %c %m %n");
		appender.setLayout(layout);
		BasicConfigurator.configure(appender);
		logger.addAppender(appender);
		
		
	
	}
	
	//type ip port  if beckend server,type=1, if primary server ,type=0,if frontend server,type=2
	public String constructNotice(boolean fromPrimary)
	{
		r.lock();
		try
		{
		String notice="";
		String servertype="";
		String ip="";
		int port=0;
		if(SocketConfig.serverType==ServerType.BackendServer)
		{
			notice=SocketConfig.identifier+" 1 "+SocketConfig.myIp+" "+String.valueOf(SocketConfig.backendPort)+" ";
		}
		else if(SocketConfig.serverType==ServerType.FrontServer)
		{
			notice=SocketConfig.identifier+" 2 "+SocketConfig.myIp+" "+String.valueOf(SocketConfig.frontendport)+" ";
		}
		else if(SocketConfig.serverType==ServerType.PrimaryServer)
		{
			notice=SocketConfig.identifier+" 0 "+SocketConfig.myIp+" "+String.valueOf(SocketConfig.primaryPort)+" ";
		}
		System.out.println("SocketConfig.identifierToServer is null ? "+(SocketConfig.identifierToServer==null));
		if(SocketConfig.identifierToServer.size()>0)
		{
			Set<String> keys=SocketConfig.identifierToServer.keySet();
			Iterator<String> iter=keys.iterator();
			while(iter.hasNext())
			{
				String identifier=iter.next();
				ip=SocketConfig.identifierToServer.get(identifier);
				ServerType type=SocketConfig.serverToType.get(ip);
				if(type==ServerType.BackendServer)
				{
					notice+=identifier+" 1 "+ip+" "+String.valueOf(SocketConfig.backendPort)+" ";
				}
				else if(type==ServerType.FrontServer)
				{
					notice+=identifier+" 2 "+ip+" "+String.valueOf(SocketConfig.frontendport)+" ";
				}
				else if(type==ServerType.PrimaryServer)
					
				{
					if(fromPrimary)
					{
						if(ip.equals(SocketConfig.myIp))
						{
							notice+=identifier+" 0 "+ip+" "+String.valueOf(SocketConfig.primaryPort)+" ";
						}
					}
					else
					{
						notice+=identifier+" 0 "+ip+" "+String.valueOf(SocketConfig.primaryPort)+" ";
					}
					
					
				}
				
			}
		}
		
		
	
		return notice;
		}
		finally{
			r.unlock();
		}
	}

	public void deleteServer(String server)
	{
		w.lock();
		try
		{
		if(SocketConfig.serverToType.containsKey(server))
		{
			SocketConfig.serverToType.remove(server);
		}
		Set keys=SocketConfig.identifierToServer.keySet();
		Iterator iter=keys.iterator();
		while(iter.hasNext())
		{
			String key=iter.next().toString();
			if (SocketConfig.identifierToServer.get(key).toString().equals(server))
			{
				SocketConfig.identifierToServer.remove(key);
				if(SocketConfig.serverToType.get(server)==ServerType.BackendServer)
				{
					SocketConfig.backendList.remove(Integer.parseInt(key));
				}
				break;
			}
		}
		}
		finally
		{
			w.unlock();
		}
		
	}
	
	public void readServers(String query)
	{
		w.lock();
		try
		{
		String result="";
		StringTokenizer st=new StringTokenizer(query);
		//SocketConfig.backendList.clear();
		//SocketConfig.serverToType.clear();
		//SocketConfig.identifierToServer.clear();
		//System.out.println("in the notification from the primary there are "+st.countTokens()+" tokens");
		System.out.println("receiving one server information "+query);
		//String primaryIp=st.nextToken();
		//String identifier="";
		while(st.hasMoreTokens())
		{
			String identifier=st.nextToken();
			System.out.println("identifier: "+identifier);
			String serverType=st.nextToken();
			String ip=st.nextToken();
			String port=st.nextToken();
			System.out.println("in read servers "+serverType+" "+ip+" "+port);
			if(serverType.equals("0"))
			{
				this.deleteServer(SocketConfig.primaryIp);/////
				SocketConfig.primaryIp=ip;
				SocketConfig.primaryPort=Integer.parseInt(port);
				//if(!((SocketConfig.serverToType.containsKey(ip))))
				//{
					SocketConfig.serverToType.put(ip, ServerType.PrimaryServer);
					System.out.println(ip+" server type : "+SocketConfig.serverToType.get(ip).toString());
					SocketConfig.identifierToServer.put(identifier, ip);
					//SocketConfig.primaryIp=ip;
				//}
			}
			else if(serverType.equals("1"))
			{
				//if(!(SocketConfig.serverToType.containsKey(ip)))
				//{
					
					SocketConfig.serverToType.put(ip, ServerType.BackendServer);
					System.out.println(ip+" server type : "+SocketConfig.serverToType.get(ip).toString());
					SocketConfig.identifierToServer.put(identifier, ip);
					
					SocketConfig.backendList.add(identifier);
				///}
				SocketConfig.backendPort=Integer.parseInt(port);
			}
			else if(serverType.equals("2"))
			{
				//if(!(SocketConfig.serverToType.containsKey(ip)))
				//{
					SocketConfig.serverToType.put(ip, ServerType.FrontServer);
					System.out.println(ip+" server type : "+SocketConfig.serverToType.get(ip).toString());
					SocketConfig.identifierToServer.put(identifier, ip);
				//}
				SocketConfig.frontendport=Integer.parseInt(port);
				
			}
			else if(serverType.equals("0"))
			{
				
				//if(!((SocketConfig.serverToType.containsKey(ip))))
				//{
					SocketConfig.serverToType.put(ip, ServerType.PrimaryServer);
					System.out.println(ip+" server type : "+SocketConfig.serverToType.get(ip).toString());
					SocketConfig.identifierToServer.put(identifier, ip);
					SocketConfig.primaryIp=ip;
				//}
				SocketConfig.primaryPort=Integer.parseInt(port);
			}
			
			System.out.println(ip+" server type : "+SocketConfig.serverToType.get(ip)+ " server ip: "+SocketConfig.identifierToServer.get(identifier));
		}
		Set servers=SocketConfig.serverToType.keySet();
		Iterator iter=servers.iterator();
		while(iter.hasNext())
		{
			String server=iter.next().toString();
			ServerType type=SocketConfig.serverToType.get(server);
			if((server.equals(SocketConfig.primaryIp))&&(type==ServerType.BackendServer))
			{
				SocketConfig.serverToType.remove(server);
				break;
			}
		}
		}
		finally
		{
			w.unlock();
		}
	}
		
}
